﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Pavilion
{
    //vector equation of a line. 
    // <r> = <r0> + t<b>. If you don't understand this, go learn Calc III!
    public struct LineVector
    {
        public Vector2 StartPosition, LineLength;
        public Vector2 EndPosition { get { return StartPosition + LineLength; } set { this.LineLength = value - StartPosition; } }

        public LineVector(Vector2 startPosition, Vector2 lineLength)
        {
            this.StartPosition = startPosition;
            this.LineLength = lineLength;
        }

        public override string ToString()
        {
            return "Start(" + StartPosition + "), End(" + EndPosition + ")";
        }

        public float DetermineYPointOfLine(float x)
        {
            if (LineLength.Y == 0 || LineLength.X == 0)
                return StartPosition.Y;

            float slope = LineLength.Y / LineLength.X;

            float returnValue = slope * x - LineLength.X + StartPosition.Y;
            return returnValue;
        }

        private static bool IsPointXWithinLineRange(float x, LineVector line)
        {
            bool oddNodes = false;

            if (x < line.StartPosition.X)
                oddNodes = !oddNodes;
            if (x < line.EndPosition.X)
                oddNodes = !oddNodes;

            return oddNodes;
        }

        public static bool Intersects(LineVector firstLine, LineVector secondLine)
        {
            Vector2 deltaStartPosition = secondLine.StartPosition - firstLine.StartPosition;

            float a = MagnitudeOf2dCrossProduct(firstLine.LineLength, secondLine.LineLength);
            float b = MagnitudeOf2dCrossProduct(deltaStartPosition, firstLine.LineLength);

            //special case that lines are colinear
            if (a == 0 && b == 0)
            {
                return IsPointXWithinLineRange(firstLine.StartPosition.X, secondLine) || IsPointXWithinLineRange(firstLine.EndPosition.X, secondLine);
            }
            else
            {
                double t = MagnitudeOf2dCrossProduct(deltaStartPosition, secondLine.LineLength) / a;
                double u = b / a;

                bool isTBetweenZeroAndOne = t <= 1 && t >= 0;
                bool isUBetweenZeroAndOne = u <= 1 && u >= 0;

                return isTBetweenZeroAndOne && isUBetweenZeroAndOne;
            }
        }

        public static bool Intersects(Rectangle rectangle, LineVector line)
        {
            //represent the corners of the rectangle as vectors
            Vector2 rectLeftTop = new Vector2(rectangle.Left, rectangle.Top);
            Vector2 rectRightTop = new Vector2(rectangle.Right, rectangle.Top);
            Vector2 rectLeftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
            Vector2 rectRightBottom = new Vector2(rectangle.Right, rectangle.Bottom);

            //use the vectors to create line segments represented by vectors
            LineVector top = new LineVector(rectLeftTop, rectRightTop - rectLeftTop);
            LineVector bottom = new LineVector(rectLeftBottom, rectRightBottom - rectLeftBottom);
            LineVector left = new LineVector(rectLeftTop, rectLeftBottom - rectLeftTop);
            LineVector right = new LineVector(rectRightTop, rectRightBottom - rectRightTop);

            //test the intersection with each line of the rectangle. 
            return LineVector.Intersects(line, top) ||
                LineVector.Intersects(line, right) ||
                LineVector.Intersects(line, bottom) ||
                LineVector.Intersects(line, left) ||
                IsInsideRectangle(rectangle, line.StartPosition.X, line.StartPosition.Y);
        }

        public static IEnumerable<LineVector> ObtainLineIntersactions(PolygonBox polygonBox, LineVector lineVector)
        {
            if (polygonBox.Count <= 1)
            {
                if (PolygonBox.IsInsidePolygon(polygonBox, lineVector.StartPosition.X, lineVector.StartPosition.Y))
                    return new List<LineVector>();
                else
                    return null;
            }

            List<LineVector> lineIntersections = new List<LineVector>();

            polygonBox.ExecuteOperationPerLine(new LineExecutionHandler((previous, current) =>
            {
                LineVector polygonLine = new LineVector(previous, current - previous);

                if (Intersects(polygonLine, lineVector))
                    lineIntersections.Add(polygonLine);
            }));

            if (lineIntersections.Count == 0)
                return null;
            else
                return lineIntersections;
        }

        public static bool Intersects(PolygonBox polygonBox, LineVector lineVector)
        {
            if (polygonBox.Count > 1)
            {
                Vector2 previousPoint = polygonBox.PositionedPointCollection.Last();

                foreach (Vector2 point in polygonBox.PositionedPointCollection)
                {
                    if (Intersects(new LineVector(previousPoint, point - previousPoint), lineVector))
                        return true;
                    previousPoint = point;
                }
            }

            return PolygonBox.IsInsidePolygon(polygonBox, lineVector.StartPosition.X, lineVector.StartPosition.Y);
        }

        public static bool IsInsideRectangle(Rectangle rectangle, float x, float y)
        {
            return x < rectangle.Right && x > rectangle.Left && y < rectangle.Bottom && y > rectangle.Top;
        }

        public static float MagnitudeOf2dCrossProduct(Vector2 firstVector, Vector2 secondVector)
        {
            return firstVector.X * secondVector.Y - firstVector.Y * secondVector.X;
        }

        public static Vector2 ObtainIntersactionPoint(LineVector lineVector, Rectangle rectangle)
        {
            //represent the corners of the rectangle as vectors
            Vector2 rectLeftTop = new Vector2(rectangle.Left, rectangle.Top);
            Vector2 rectRightTop = new Vector2(rectangle.Right, rectangle.Top);
            Vector2 rectLeftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
            Vector2 rectRightBottom = new Vector2(rectangle.Right, rectangle.Bottom);

            //use the vectors to create line segments represented by vectors
            LineVector top = new LineVector(rectLeftTop, rectRightTop - rectLeftTop);
            LineVector bottom = new LineVector(rectLeftBottom, rectRightBottom - rectLeftBottom);
            LineVector left = new LineVector(rectLeftTop, rectLeftBottom - rectLeftTop);
            LineVector right = new LineVector(rectRightTop, rectRightBottom - rectRightTop);

            Vector2 rectangleCenterPostion = default(Vector2);

            if (PolygonBox.IsInsideRectangle(rectangle, lineVector.StartPosition))
                rectangleCenterPostion = lineVector.StartPosition;
            else if (PolygonBox.IsInsideRectangle(rectangle, lineVector.EndPosition))
                rectangleCenterPostion = lineVector.EndPosition;

            if (Intersects(lineVector, top))
            {
                float slope = -(lineVector.LineLength.X / lineVector.LineLength.Y);
                int height = rectangle.Height / 2;

                return new Vector2(slope * height + rectangleCenterPostion.X, rectangle.Top);
            }
            else if (Intersects(lineVector, bottom))
            {
                float slope = lineVector.LineLength.X / lineVector.LineLength.Y;
                int height = rectangle.Height / 2;

                return new Vector2(slope * height + rectangleCenterPostion.X, rectangle.Bottom);
            }
            else if (Intersects(lineVector, left))
            {
                float slope = -(lineVector.LineLength.Y / lineVector.LineLength.X);
                int width = rectangle.Width / 2;

                return new Vector2(rectangle.Left, slope * width + rectangleCenterPostion.Y);
            }
            else if (Intersects(lineVector, right))
            {
                float slope = lineVector.LineLength.Y / lineVector.LineLength.X;
                int width = rectangle.Width / 2;

                return new Vector2(rectangle.Right, slope * width + rectangleCenterPostion.Y);
            }

            return default(Vector2);
        }
    }
}
